package com.wisdom.client.discovery.nacos;

import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.naming.listener.Event;
import com.alibaba.nacos.api.naming.listener.EventListener;
import com.alibaba.nacos.api.naming.listener.NamingEvent;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.api.naming.pojo.ListView;
import com.wisdom.client.cache.NacosServiceProvider;
import com.wisdom.client.cache.NacosServiceProviderCache;
import com.wisdom.client.config.ClientRpcConfig;
import com.wisdom.client.discovery.RpcServerDiscovery;
import com.wisdom.core.exception.RpcException;
import com.wisdom.core.nacos.NacosServerRegister;
import com.wisdom.core.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @description: <基于Nacos的服务发现>
 * @author: Leo
 * @date:2021-08-22 16:32
 */
@Component
@Slf4j
public class NacosRpcServiceDiscovery implements RpcServerDiscovery<NacosServiceProvider, Instance> {

    @Autowired
    private ClientRpcConfig clientRpcConfig;

    @Autowired
    private NacosServerRegister nacosServerRegister;

    @Autowired
    private NacosServiceProviderCache cache;


    @Override
    public void serverDiscovery() {
        try {
            ListView<String> allServiceName = nacosServerRegister.getServicesOfServer(clientRpcConfig.getGroupName());
            List<String> serviceNames = allServiceName.getData();
            if (serviceNames != null) {
                for (String serviceName : serviceNames) {
                    List<Instance> instances = nacosServerRegister.selectHealthyInstances(serviceName, clientRpcConfig.getGroupName());
                    List<NacosServiceProvider> serviceProviders = getServiceProvider(instances);
                    cache.put(serviceName, serviceProviders);
                    nacosServerRegister.subscribe(
                            serviceName,
                            Collections.singletonList(clientRpcConfig.getClusterNames()),
                            clientRpcConfig.getGroupName(),
                            new UpdateCacheEventListener());
                    log.info("client discovery and subscribe service,service name is {}", serviceName);
                }
            }
        } catch (Exception e) {
            log.info("client discovery service error, msg is {}", e.getMessage());
            throw new RpcException(e.getMessage(), e);
        }
    }

    @Override
    public List<NacosServiceProvider> getServiceProvider(List<Instance> instances) {
        return instances.stream().map(instance -> {
            NacosServiceProvider nacosServiceProvider = new NacosServiceProvider();
            nacosServiceProvider.setServiceName(instance.getServiceName());
            nacosServiceProvider.setServerIp(instance.getIp());
            nacosServiceProvider.setServerPort(instance.getPort());
            nacosServiceProvider.setEnabled(instance.isEnabled());
            nacosServiceProvider.setGroupName(clientRpcConfig.getGroupName());
            nacosServiceProvider.setInstanceId(instance.getInstanceId());
            nacosServiceProvider.setClusterNames(instance.getClusterName());
            nacosServiceProvider.setHealthy(instance.isHealthy());
            nacosServiceProvider.setWeight(instance.getWeight());
            return nacosServiceProvider;
        }).collect(Collectors.toList());
    }


    class UpdateCacheEventListener implements EventListener {

        @Override
        public void onEvent(Event event) {
            NamingEvent updateCacheEvent = (NamingEvent) event;
            log.info("receiver event, event is {}", JSON.toJSONString(updateCacheEvent));
            List<Instance> allInstances = nacosServerRegister.getAllInstances(updateCacheEvent.getServiceName(),
                    StringUtils.subString(updateCacheEvent.getClusters()), updateCacheEvent.getGroupName());
            if (allInstances == null || allInstances.isEmpty()){
                cache.evict(updateCacheEvent.getServiceName());
                return;
            }
            List<NacosServiceProvider> serviceProvider = getServiceProvider(allInstances);
            cache.update(updateCacheEvent.getServiceName(), serviceProvider);
        }
    }

}
